#include <stdlib.h>  // standard lib functions
#include <stddef.h>  // standard definitions
#include <stdint.h>  // standard integer definition
#include <stdbool.h> // boolean definition
#include <stdio.h>

#include "peripheral.h"
#include "bootloader.h"
#include "app_xmode.h"
#include "App_lib.h"
#define FIRMWARE_VER "V105_20230912"

// const uint8_t fw_version_infor_code[32] __attribute__((section(".ARM.__at_0x00407f00"))) = {FIRMWARE_VER}; // RO

#define dbg(format, ...) printf(format "\n", ##__VA_ARGS__)
/*********************************************************************
 * MACROS
 */

#define PIN_GPIO2 2
#define PIN_GPIO3 3
#define PIN_UART0_TX 4
#define PIN_UART1_TX 5//
#define PIN_UART1_RX 6//

#define BAUDRATE_DEBUG 115200
#define BAUDRATE_OTA 460800

/*********************************************************************
 * LOCAL FUNCTIONS
 */
void WDT_FeedDog(void)
{
    wdt_keepalive();
}
static uint32_t SysTickCount;
uint32_t SysTick_GetTick(void)
{
    return SysTickCount;
}
/**
 * @brief tim_timer_handler()
 *
 * @return
 **/

static void tim_timer_handler(void)
{
    SysTickCount++;
}

static void Timer0_Init(void)
{
    const tim_config_t timer_cfg =
        {
            .mode = TIM_TIMER_MODE,
            .config.timer =
                {
                    .period_us = 1000,
                    .callback = tim_timer_handler,
                },
        };
    // Timer init

    tim_config(HS_TIM0, &timer_cfg);
    tim_start(HS_TIM0);
}
static void Timer0_DeInit(void)
{
    tim_stop(HS_TIM0);
}
#define UART_RECV_MAX_LEN 0x200
typedef struct _uart_recv_buf
{
    uint16_t rp;                    // read pointer
    uint16_t wp;                    // write pointer
    uint8_t dat[UART_RECV_MAX_LEN]; // buffer
} UartRecvBufStruct;

static UartRecvBufStruct g_Uart1RecvBufStruct = {0, 0}; // uart1 buffer

void UartComm_Send(uint8_t *buff, uint16_t size)
{
    uart_send_block(HS_UART1, buff, size);
}
uint16_t Get_RxBuf_Nun(void)
{
    return (g_Uart1RecvBufStruct.wp - g_Uart1RecvBufStruct.rp + UART_RECV_MAX_LEN) % UART_RECV_MAX_LEN;
}
bool UART_RecvByte(uint8_t *dat)
{
    if (g_Uart1RecvBufStruct.wp != g_Uart1RecvBufStruct.rp)
    {
        *dat = g_Uart1RecvBufStruct.dat[g_Uart1RecvBufStruct.rp];
        g_Uart1RecvBufStruct.rp += 1;

        if (g_Uart1RecvBufStruct.rp >= UART_RECV_MAX_LEN)
            g_Uart1RecvBufStruct.rp = 0;
        return TRUE;
    }
    return FALSE;
}
uint16_t UartComm_Read_alldata(uint8_t *buf)
{
    uint8_t data = 0x00;
    uint16_t cnt = 0;

    while (TRUE == UART_RecvByte(&data))
    {
        *buf = data;
        cnt++;
        *buf++;
    }

    return cnt;
}
static void Uart1_read_handler(uint8_t data)
{
    g_Uart1RecvBufStruct.dat[g_Uart1RecvBufStruct.wp] = data;
    g_Uart1RecvBufStruct.wp += 1;
    if (g_Uart1RecvBufStruct.wp >= UART_RECV_MAX_LEN)
        g_Uart1RecvBufStruct.wp = 0;
}
static void peripheral_init(void)
{
    // Init GPIO
    gpio_open();
	#ifdef USE_UART
    // Init UART
    pinmux_config(PIN_UART0_TX, PINMUX_UART0_SDA_O_CFG); // tx
    pmu_pin_mode_set(BITMASK(PIN_UART0_TX), PMU_PIN_MODE_PP);
    uart_open(HS_UART0, BAUDRATE_DEBUG, UART_FLOW_CTRL_DISABLED, NULL);
	#endif
    pinmux_config(PIN_UART1_TX, PINMUX_UART1_SDA_O_CFG); // tx
    pmu_pin_mode_set(BITMASK(PIN_UART1_TX), PMU_PIN_MODE_PP);

    pinmux_config(PIN_UART1_RX, PINMUX_UART1_SDA_I_CFG); // rx
    pmu_pin_mode_set(BITMASK(PIN_UART1_RX), PMU_PIN_MODE_PU);

    uart_open(HS_UART1, BAUDRATE_OTA, UART_FLOW_CTRL_DISABLED, Uart1_read_handler);
}

#define LED_RED_PIN 20 //
#define LED_BLUE_PIN 21
#define LED_LOG_B_PIN 28
#define LED_LOG_W_PIN 29
#define LED_LOG_R_PIN 30

#define LEDS_HIGH_PINS ((1 << LED_RED_PIN) | (1 << LED_BLUE_PIN) | (1 << LED_LOG_B_PIN) | (1 << LED_LOG_W_PIN) | (1 << LED_LOG_R_PIN))

static void hardware_init(void)
{
    pmu_pin_mode_set(0xFFFFFFFF & (~LEDS_HIGH_PINS), PMU_PIN_MODE_PD);
    pmu_pin_mode_set(LEDS_HIGH_PINS, PMU_PIN_MODE_PU);
#ifndef CONFIG_GPIO1_ABSENT
    pmu_pin_mode_set_ex(0xFF, PMU_PIN_MODE_PU);
#endif

    peripheral_init();
    Timer0_Init();
    sf_enable(HS_SF, 0);
}

static void hardware_Deinit(void)
{
#ifndef CONFIG_GPIO1_ABSENT
    pmu_pin_mode_set_ex(0xFF, PMU_PIN_MODE_PU);
#endif
    __disable_irq();
    Timer0_DeInit();
    uart_close(HS_UART1);
    pmu_pin_mode_set(0xFFFFFFFF & (~LEDS_HIGH_PINS), PMU_PIN_MODE_PD);
    pmu_pin_mode_set(LEDS_HIGH_PINS, PMU_PIN_MODE_PU);
}
__WEAK void sys_clock_init(void)
{
    #ifdef CONFIG_HS6621P
    cpm_set_clock_div(CPM_SF1_CLK, 2);
    cpm_set_clock_div(CPM_SF0_CLK, 2);
    cpm_set_clock_div(CPM_CPU_CLK, 0);
    pmu_pll_startup(128);
    #elif  CONFIG_HS6621C
    cpm_set_clock_div(CPM_CPU_CLK,1);
    pmu_xtal32m_x2_startup();
    #endif
}
static void Bootloader_JumpToApp(uint32_t app_addr)
{
    uint32_t jump_to_application;
    uint32_t jump_address;
    jump_address = *(uint32_t *)(app_addr);
    jump_to_application = *(uint32_t *)(app_addr + sizeof(uint32_t));
    // Jump to APP
    __set_MSP(jump_address);
    (*((void (*)())(jump_to_application)))();
}

extern void sys_clock_init(void);

#define FLASH_PAGE_SIZE (4096)

//读操作
inline static int ee_read_flash(uint32_t addr, uint8_t *data, uint32_t len)
{
    sf_read(HS_SF, 0, (addr & 0xfffff), data, len);
    return 0;
}

//写操作
inline static int ee_write_flash(uint32_t addr, uint8_t *data, uint32_t len)
{
    sf_write(HS_SF, 0, (addr & 0xfffff), data, len);
    return 0;
}

//擦除操作
inline static int ee_erase_sector(uint32_t addr)
{
    sf_erase(HS_SF, 0, (addr & 0xfffff), 4 * 1024);
    return 0;
}
int Flash_Write(uint32_t iAddress, uint8_t *buf, uint32_t iNbrToWrite)
{
    /* Unlock the Flash Bank1 Program Erase controller */
    uint32_t secpos; //扇区地址
    uint32_t NumByteToWrite = iNbrToWrite;
    uint16_t secoff;    //在扇区内的偏移
    uint16_t secremain; //扇区剩余空间大小
    uint16_t i = 0;
    uint8_t tmp[FLASH_PAGE_SIZE];

    secpos = iAddress & (~(FLASH_PAGE_SIZE - 1)); //扇区地址
    secoff = iAddress & (FLASH_PAGE_SIZE - 1);    //在扇区内的偏移
    secremain = FLASH_PAGE_SIZE - secoff;         //扇区剩余空间大小

    if (NumByteToWrite <= secremain)
        secremain = NumByteToWrite; //不大于4096个字节
    while (1)
    {
        ee_read_flash(secpos, tmp, FLASH_PAGE_SIZE); //读出整个扇区

        for (i = 0; i < secremain; i++)
        {
            //校验数据
            if (tmp[secoff + i] != 0XFF)
                break; //需要擦除
        }

        if (i < secremain)
        {
            //需要擦除
            ee_erase_sector(secpos); //擦除这个扇区

            for (i = 0; i < secremain; i++)
            {
                //复制
                tmp[i + secoff] = buf[i];
            }

            ee_write_flash(secpos, tmp, FLASH_PAGE_SIZE); //写入整个扇区
        }
        else
        {
            ee_write_flash(iAddress, buf, secremain); //写已经擦除了的,直接写入扇区剩余区间.
        }

        if (NumByteToWrite == secremain) //写入结束了
            break;
        else
        {
            secpos += FLASH_PAGE_SIZE;
            secoff = 0;                  //偏移位置为0
            buf += secremain;            //指针偏移
            iAddress += secremain;       //写地址偏移
            NumByteToWrite -= secremain; //字节数递减

            if (NumByteToWrite > FLASH_PAGE_SIZE)
                secremain = FLASH_PAGE_SIZE; //下一个扇区还是写不完
            else
                secremain = NumByteToWrite; //下一个扇区可以写完了
        }
    }
    return iNbrToWrite;
}

void Flash_Read(uint32_t add, uint8_t *buf, uint16_t len)
{
    ee_read_flash(add, buf, len);
}
#define TEST_SIZE 1024 * 6
uint8_t write_buffer[TEST_SIZE] = {0};
uint8_t read_buffer[TEST_SIZE] = {0};
void example_sf(uint32_t add)
{

    uint32_t i;
    Flash_Read(add, &i, 4);
    i++;
    memset((uint32_t *)write_buffer, i, TEST_SIZE / 4);
    // Write 100 bytes to 128K
    Flash_Write(add, write_buffer, TEST_SIZE);
    dbg("Write");
    dbg_hex(write_buffer, 8);
    // Read 100 bytes in 128K, it should be same as write_buffer
    Flash_Read(add, read_buffer, TEST_SIZE);
    dbg("Read2");
    dbg_hex(read_buffer, 8);
    if (lib_mem_cmp(read_buffer, write_buffer, TEST_SIZE))
    {
        dbg("example_sf success");
    }
    else
    {
        dbg("example_sf error");
    }
}
void Flash_test(void)
{
    while (1)
    {
        example_sf(128 * 1024);
        co_delay_ms(1000);
    }
}
ErrorStatus AppCode_Check(uint32_t add, uint32_t len)
{
    uint32_t appCrc = 0, flashCode_Crc = 0;
    uint32_t appVerifyStartAddress = add;
    uint32_t appcodeSize = len - __APP_SIGNATURE_SIZE;
    uint8_t buf[EXFLASH_PAGE_SIZE] = {0};
    uint16_t cnt = 0;

    Flash_Read(appVerifyStartAddress + appcodeSize, (uint8_t *)&flashCode_Crc, 4);

    while (appcodeSize)
    {
        uint16_t size = 0;
        if (appcodeSize > EXFLASH_PAGE_SIZE)
            size = EXFLASH_PAGE_SIZE;
        else
            size = appcodeSize;
        Flash_Read(appVerifyStartAddress + cnt * EXFLASH_PAGE_SIZE, buf, size);
        appCrc = CalcCRC32(buf, size, appCrc);
        appcodeSize -= size;
        cnt++;
    }
    dbg("[AppCode_Check] add: %X len: %X", add, len);
    dbg("appCrc: %X flashCode_Crc: %X", appCrc, flashCode_Crc);

    if (appCrc == flashCode_Crc)
    {
        return SUCCESS;
    }

    return ERROR;
}

void UpdCode_Exflash_2_APP(void)
{
    uint16_t i;
    uint8_t tmp[EXFLASH_PAGE_SIZE];
    // Enable flash
    for (i = 0; i < APP_FLASH_SIZE / EXFLASH_PAGE_SIZE; i++)
    {
        // Read OTA_CODE_ADDR
        Flash_Read(OTA_CODE_ADDR + EXFLASH_PAGE_SIZE * i, tmp, EXFLASH_PAGE_SIZE);
        Flash_Write(APP_CODE_ADDR + EXFLASH_PAGE_SIZE * i, tmp, EXFLASH_PAGE_SIZE);
    }
}

ErrorStatus BootLoader_Compare_Ver(void)
{
    uint8_t tmp[32 + 1];
    uint8_t appfw[32 + 1] = {0}, apphw[32 + 1] = {0}, appDevType[32 + 1] = {0};
    uint8_t Exfw[32 + 1] = {0}, Exhw[32 + 1] = {0}, ExDevType[32 + 1] = {0};

    Flash_Read(APP_FW_VERSION_INFOR_ADDR, appfw, 32);
    Flash_Read(APP_HW_VERSION_INFOR_ADDR, apphw, 32);
    Flash_Read(DEVICE_TYPE_INFOR_ADDR, appDevType, 32);

    uint32_t otaInforStartAddr = OTA_CODE_ADDR + APP_FLASH_SIZE - 0x100;

    Flash_Read(otaInforStartAddr + 0, Exfw, 32);
    Flash_Read(otaInforStartAddr + 32, Exhw, 32);
    Flash_Read(otaInforStartAddr + 64, ExDevType, 32);

    dbg("[BootLoader_Compare_Ver]");
    dbg("appfw:%s Exfw:%s", appfw, Exfw);
    dbg("apphw:%s Exhw:%s", apphw, Exhw);
    dbg("appDevType:%s ExDevType:%s", appDevType, ExDevType);

    memset(tmp, 0xff, 32);

    if (lib_mem_cmp(appfw, tmp, 32)) //没有固件强制升级
    {
        dbg("no add code must upd");
        return SUCCESS;
    }

    if ((0 != strcmp(appfw, Exfw))               // FW 不同
        && (0 == strcmp(apphw, Exhw))            // HW 相同
        && (0 == strcmp(appDevType, ExDevType))) // DevType 相同
    {
        dbg("appfw no same will upd...");
        return SUCCESS;
    }

    if (0 == strcmp(appfw, Exfw)) // FW 相同
    {
        uint8_t tmp1[0x100] = {0};

        if (SUCCESS == AppCode_Check(APP_CODE_ADDR, APP_FLASH_SIZE))
        {
            //烧录成功则擦除版本信息
            Flash_Write(OTA_CODE_ADDR + APP_FLASH_SIZE - 0x100, tmp1, 0x100);
            dbg("appfw same will Earse ExFlash code infor");
            return ERROR;
        }
        else
        {
            //固件更新失败，重新更新
            dbg("AppCode_Check err ,upd again");
            return SUCCESS;
        }
    }

    dbg("code err ");

    return ERROR;
}

int main(void)
{
    /* 系统配置，获取时钟相关参数等 */
    sys_clock_init();
    hardware_init();
    printf("Bootloader VER:%s\r\n", FIRMWARE_VER);
    // Flash_test();
    wdt_enable(10);
    __enable_irq();
    /* Debug串口初始化 */
    AppXmode();

    if (SUCCESS == BootLoader_Compare_Ver()) // FW校验为不相同且HW跟DevTpye相同,则进行下一步
    {
        if (SUCCESS == AppCode_Check(OTA_CODE_ADDR, APP_FLASH_SIZE)) //固件CRC 通过才进行固件更新
        {
            __disable_irq();
            UpdCode_Exflash_2_APP();
            __enable_irq();
        }
    }

    if (SUCCESS == AppCode_Check(APP_CODE_ADDR, APP_FLASH_SIZE))
    {
        printf("CRC32 succ Jump To App:%X!!!\n", APP_CODE_ADDR);
        hardware_Deinit();
        Bootloader_JumpToApp(APP_CODE_ADDR);
    }
}
